/*=============================================================================
#    The element-comm is a tentative program used to test our ideas.           
#                       Copyright (C)  bgao                                    
#
#     This program is free software; you can redistribute it and/or            
#      modify it under the terms of the GNU General Public License             
#     as published by the Free Software Foundation; either version 2           
#         of the License, or (at your option) any later version.               
#
#     This program is distributed in the hope that it will be useful,          
#    but WITHOUT ANY WARRANTY; without even the implied warranty of           
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            
#             GNU General Public License for more details.                    
#
#    You should have received a copy of the GNU General Public License        
#     along with this program; if not, write to the Free Software             
#      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,             
#                       MA  02110-1301, USA.                                  
#==============================================================================
# $Id: mac_indir.c 348 2010-11-29 09:01:16Z bgao $ 
#
# $Author: bgao $ 
#
# $Revision: 348 $ 
#
# $Date: 2010-11-29 17:01:16 +0800 (星期一, 29 十一月 2010) $ 
#
# Description: 
#
=============================================================================*/
/*! @file mac_indir.c
 *  @brief This module implements the mac indirect transmission mechanism.
 */
#include <string.h>
#include <stdlib.h>
#include "mac.h"

/*! define the size of indirect buffer */
#define MAC_INDIR_BUF_COUNT 10

/*! define a mac_indir list */
LIST(mac_indir);
/*! define a memory used to store the mac indir */
MEM(mi_free_list);
/*! define the mac_indir pool */
mac_indir_t mac_indir_pool[MAC_INDIR_BUF_COUNT];
/*! define the mac_indir timeout timer */
static mtimer_t indir_timer;

static void mac_indir_timeout(int8 signum);

/*! initialize the mac indirect queue.
 *  @return MAC_ERR(-1) if the list or the mem sub module malfunction
            else return MAC_OK(1).
 */
int8 mac_indir_init(void)
{
    int r, t;
    pool_t p;

    p.ptr = mac_indir_pool;
    p.num = MAC_INDIR_BUF_COUNT;
    p.size = sizeof(mac_indir_t);

    memset(mac_indir_pool, 0, sizeof(mac_indir_pool));
    r = list_init(mac_indir);
    t = mem_init(mi_free_list, &p);

    /* start timer */
    timer_init(&indir_timer, mac_indir_timeout);
    /* use the timer which has a bigger resolusion, then the time out time should be adjusted */
    /* TODO select a suitable timeout time when the tick is set */
    timer_start(&indir_timer, 1);

    if (r == MAC_ERR || t == MAC_ERR)
        return MAC_ERR;
    else
        return MAC_OK;
}

/*! allocate a entry from the mac indirect queue.
 *  @return pointer to the allocated entry, return NULL if no free entry.
 */
mac_indir_t *mac_indir_alloc(void)
{
    mac_indir_t *m;
    uint32 r;


    m = mem_alloc(mi_free_list);
    if (m == NULL) {
        return NULL;
    }
    m->timeout = mac_get_transaction_persistence_time();

    r = list_add_head(mac_indir, m);
    if (r == MAC_ERR) {
        return NULL;
    }

    m = list_get_head(mac_indir);

    return m;
}

/*! free the allocated entry from mac indirect queue.
 *  @param dsn the data sequence number of the entry to be freed.
 *  @return return MAC_ERR(-1) if no such entry exists else return MAC_OK(0).
 */
int8 mac_indir_free(uint8 dsn)
{
    mac_indir_t *m = list_get_head(mac_indir);

    for (; m != NULL; m = m->next) {
        if (m->dsn == dsn) {
            break;
        }
    }

    if (m == NULL) {
        return MAC_ERR;
    } else {
        list_del(mac_indir, m);
        return mem_free(mi_free_list, m);
    }
}

/*! find the entry whose address is specified in addr.
 *  @param addr pointer to the structure of address.
 *  @return pointer to the entry whose address is match with address specified by addr.
 */
mac_indir_t *mac_indir_find(addr_t *addr)
{
    mac_indir_t *m = list_get_head(mac_indir);

    if (addr == NULL || m == NULL) {
        return NULL;
    }

    for (; m != NULL; m = m->next) {
        if (addr->mode == ADDR_16_M) {
            if ((m->addr).a.addr16 == (addr->a).addr16) {
                break;
            }
        } else {
            if ((m->addr).a.addr64 == (addr->a).addr64) {
                break;
            }
        }
    }

    if (m == NULL) {
        return NULL;
    } else {
        return m;
    }
}


/*! find the entry whose dsn is specified in dsn.
 *  @param dsn data sequence number.
 *  @return pointer to the entry whose dsn is match with dsn.
 */
mac_indir_t *mac_indir_find_by_dsn(uint8 dsn)
{
    mac_indir_t *m = list_get_head(mac_indir);

    if (m == NULL) {
        return NULL;
    }

    for (; m != NULL; m = m->next) {
        if (dsn == m->dsn) {
            break;
        }
    }

    if (m == NULL) {
        return NULL;
    } else {
        return m;
    }
}

/*! the expiry function
 *  It will be triggered periodically, during each time of run
 *  it will check to see if there are any items timeout, if there
 *  are, those will be freed form the indir buffer.
 *  @param signum the parameter passed to this function.
 */
static void mac_indir_timeout(int8 signum)
{
    /* check if there are indir items are time out */
    mac_indir_t *m = list_get_head(mac_indir);

    if (m == NULL) {
        timer_start(&indir_timer, 1);
        return;
    }

    for (; m != NULL; m = m->next) {
        if (m->timeout != 0) {
            m->timeout--;
        }
        if (m->timeout == 0) {
            /* TODO signal the nhl that the transaction is expired */
            list_del(mac_indir, m);
            mem_free(mi_free_list, m);
        }

    }
    /* TODO select a suitable timeout time when the tick is set */
    timer_start(&indir_timer, 1);
}
